package com.c0nflict.server;

import java.util.Collection;
import java.util.Date;
import java.sql.Timestamp;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.directwebremoting.ScriptBuffer;
import org.directwebremoting.ScriptSession;

import com.c0nflict.server.Player;

public class ServerAction {
	
	public void serverTurnOver(TableManager tables, Table table, int user) {
		if (user >= table.getMaxPlayers()) {
			System.out.println("serverTurnOver: WTFPWN");
			table.setMaxPlayers(user+1);
			//return;
		}

		if (table.isNotRunning()) return;

		int largestArea = table.calcLargestArea(user);
		largestArea*=.75;

		if (table.players[user].getTotalDice() < 8 && largestArea <= 1) {
			
			//if (table.isRunning())
			//table.moveToNextPlayer();
			largestArea=3;
		} else if (largestArea<=1) {
			largestArea=1;
		}
		
		Player player = table.players[user];
		player.addTotalEarned(largestArea);
		table.gameStats.addTotalEarned(user, largestArea);
		
		table.logger.logEvent(player.getName(), "turnover", "la:"+largestArea);
		
		int dice = largestArea + player.getPlusDice(); 
		player.idle();

		table.attackSquare = -1;
		table.defendSquare = -1;
		
		ScriptBuffer script = new ScriptBuffer();
		
		//boolean newRound = false;
		int currentRound = table.getCurrentTurn();
		
		if (player.getOrderId() != table.getCurrentPlayerId())
				return; // WE WANT THIS

			//lets use the watchdog ? no, cant.
		table.moveToNextPlayer();

		if (table.getCurrentTurn() != currentRound)
			script.appendScript("round_change(" + table.getCurrentTurn() + ");");
		
		// Randomly restack them. WHEW
		int full = 0;

		//TODO Change to randomly assign dice based on the player's squares vector
		StringBuffer buf = new StringBuffer("restack_dice("+table.getJSID()+",Array(");
		boolean firstTime = true;
		while (dice > 0) {
			full++;
			if (full > 10000) {
				// we're full!!
				player.setPlusDice(dice);
				break;
			}

			int x = tables.R.randomBaseZero(table.getTableSize());
			
			if (table.squares[x].getOwner() == player && table.squares[x].getDice() + 1 <= table.getMaxDicePerSquare()) {
				table.squares[x].addDice(1);
				
				if (!firstTime)
					buf.append(",");
				
				firstTime = false;
				
				buf.append(x).append(",").append(table.squares[x].getDice());
				dice--;
			}
			
		}
		
		buf.append("));");
		
		if (!firstTime) 
			script.appendScript(buf.toString());
		
		if (table.isRunning())
			script.appendScript("turnChange("+table.getJSID()+","+ player.getColor() + "," + 
				table.players[table.getCurrentPlayerId()].getColor() + "," + largestArea+","+ dice + ");");
		
		int movableAreas = (int)(table.getMovableAreasPercent() * player.getAreaCount());
		if (player!=null) {
		 player.setMovableAreas(movableAreas);
		if (player.getTotalDice() > 0)
			if (movableAreas > 0 && player.getAreaCount() / player.getTotalDice() < table.getMaxDicePerSquare()) {
				ScriptBuffer ps = new ScriptBuffer();
				ps.appendScript("sendStatus('<b>You may move armies between " + movableAreas + " connected (non diagonal) spaces.</b><br/>');");
				ps.appendScript("document.getElementById('offturn_moves').innerHTML='Offturn moves remaining: " + movableAreas + "';");
				player.getSession().addScript(ps);
			}
		}
		table.players[table.getCurrentPlayerId()].setMovableAreas(0);
		
		if (table.attackSquare >= 0) script.appendScript("undepress_square(" + table.attackSquare + "," + player.getColor() + ");");
		//System.out.println("user:"+user+ " "+page+" "+script.toString());
		player.setPlusDice(dice);
		if (player.shouldAutoFlag()) {
			table.logger.logEvent(player.getName(), "iplay", ""+table.tableEloCount);
			serverPlayerDeath(tables, table, user);
		}
		tables.sendJavascript(table, script);
		table.resetClock();
		table.moveAutomaton();
	}
	   
	public double adjustELO(Table table, Player player,double ELOAvg, int winloss) {
		double ELO_SPAN_100 = 400;
		double ELO_BONUS_LAST = 20;
		int adjust = 0;
		double place = 0;
        if (player == null) return 0;
        
		int K;
		int diff = (int)Math.abs(player.getELO()-ELOAvg);
		
		if (diff < 100) K = 70;
		else if (diff < 250) K = 50;
		else if (diff < 400) K = 30;
		else K = 20;
		place = 1;
	 /*	if (player.getOrderId() > table.getFirstPlayerId())
			place = table.getFirstPlayerId() - player.getOrderId();
		else
			place = (table.getMaxPlayers() - table.getFirstPlayerId()) + player.getOrderId();
	 */
		double expected_score = (player.getELO() - (ELO_BONUS_LAST / place)) - ELOAvg;
		// this nets to player.elo - averageelo (300 - 500) = -200 (player 100 less than average)
		double expval = 0;
		if (expected_score > ELO_SPAN_100) expval = 1;
		else if (expected_score < -1 * ELO_SPAN_100 + ELO_BONUS_LAST)
			expval = 0;
		else
			expval = ((double)(ELO_SPAN_100 + expected_score)) * .5 / ELO_SPAN_100;
		//expval = 400+-200==200 *.5 / 400 -- we expect a win 100/400 or 25% of the time
		//System.out.print(" expval:" + expval);

		adjust = (int)Math.round(player.getELO() + K * (winloss - expval));
		// K val (max adjustment based on difference between this player and average)
		// times (1 or 0 (win/loss) - expval)
		// K=100 -- 100* (1 - .25) if they win adjustment is 75
		// K=100 -- 100* (0 - .25) if they lose adjustment is -25;
		return adjust;
	}
	   
	   
	public void serverPlayerDeath(TableManager tables, Table table, int died) {
		serverPlayerDeath(tables,table,died,false);
	}
	public void serverPlayerDeath(TableManager tables, Table table, int died, boolean flagged) {
		// Broadcast a message about this persons timely or untimely death.
		if (table.isNotRunning()) return;
		int playercount=0;
		
		ScriptBuffer script = new ScriptBuffer(); 
		for (int x=0;x<table.getMaxPlayers();x++) {
			if (table.players[x].isPlayer()) playercount++;
		}
		if (playercount>=2) {
			Player whodied = table.players[died];
			if (whodied.isNobody())
				table.killPlayer(whodied);
			table.logger.logEvent(whodied.getName(), "playerdeath", ""+table.tableEloCount);
	
			//TODO Refactor some code into Table class - order of actions is IMPORTANT!!!
			// Remove squares from map.
			table.tableTotalElo -= whodied.getELO();
			table.tableEloCount--;
			int adjustedELO = 0;
			// ADJUTS ELO
			adjustedELO =
				(int)(
						((double)table.deathEloCount * 
								adjustELO(table, whodied, table.totalDeathElo /(double)table.deathEloCount,1 ) + 
								table.tableEloCount * adjustELO(table, whodied, table.tableTotalElo / (double)table.tableEloCount, 0)
						) / ((double)(table.deathEloCount+table.tableEloCount))
				);
			/*System.out.println(table.deathEloCount + " " + 
					adjustELO(table, whodied, table.deathEloCount / (double)table.deathEloCount, 1) + " " +
					table.tableEloCount + " " + 
					adjustELO(table, whodied, table.tableTotalElo / (double)table.tableEloCount, 0) + " " + adjustedELO);
			*/
			//ADJUST ELO AGAIN FOR DOMINANCE.
			if (tables.useDOM && table.getCurrentTurn()>5 && playercount>1) {
				try {
				double largerThanMe_elo = 0;
				double largerThanMe_count = 0;
				double smallerThanMe_elo = 0;
				double smallerThanMe_count = 0;
				for (int x=0;x<6;x++) {
					if (table.players[x]!=whodied && table.players[x].isPlayer()) {
						if (table.players[x].getTotalEarned()>whodied.getTotalEarned()) {
							largerThanMe_elo+=table.players[x].getELO();
							largerThanMe_count++;
						} else if (table.players[x].getTotalEarned()<whodied.getTotalEarned()) {
							smallerThanMe_elo+=table.players[x].getELO();
							smallerThanMe_count++;
						}
					}
				}
				
				int adjustedDOM =
					(int)(
							((double)smallerThanMe_count * 
									adjustELO(table, whodied, smallerThanMe_elo /smallerThanMe_count,1 ) + 
									largerThanMe_count * adjustELO(table, whodied, largerThanMe_elo/largerThanMe_count, 0)
							) / ((double)(smallerThanMe_count+largerThanMe_count))
					);
				
				//70 / 30 placement to dom?
			if (smallerThanMe_count+largerThanMe_count>0) {
				if (playercount==2) {
					if (adjustedDOM<0) adjustedDOM=0;
					adjustedELO =(int)( adjustedELO*.8+adjustedDOM*.2);
				}
				else
					adjustedELO =(int)( adjustedELO*.6+adjustedDOM*.4);
			}
			
			} catch (Exception e) {e.printStackTrace();}
			} 
	
			
			flagged=false;
			adjustedELO+=4;
			table.totalDeathElo += whodied.getELO();
			table.deathEloCount++;
			int ELOch = whodied.getELO() - adjustedELO;

			if (!table.isRated()) {
				adjustedELO=whodied.getELO();
				ELOch = 0;
			}
					
			table.gameStats.setPlayerDeathTime(died, new Date());
			table.gameStats.setPlayerELOChange(died, ELOch);
			table.gameStats.setPlayerFinishPlace(died, table.tableEloCount);
	
			//System.out.println("ADJ ELO HERE");
			//int spaces = whodied.getAreaCount();
			//System.out.println("KP" + spaces + whodied.getColor());
			whodied.setELO(adjustedELO);
			table.players[died] = table.tableSeats[died];
			table.killPlayer(whodied);
	
			script.appendScript("playerDeath(" + whodied.getColor() + ");");
			serverChairEvent(tables, whodied);
			script.appendScript("sendStatus('<h3>" + whodied.getName() + " has fallen.</h3>Rating change: " +(ELOch<0?"+":"") +(-ELOch) + " to " + whodied.getELO() + (flagged?", 5pt Surrender Bonus":"") + "<br><br>');");
			String imgsrc = Rank.getRankImageSmall(whodied.getELO());
			String imgtitle = Rank.getRankName(whodied.getELO());
			String thStr = "th";
			
			if ((table.tableEloCount + 1) == 1)
				thStr = "st";
			else if ((table.tableEloCount + 1) == 2)
				thStr = "nd";
			else if ((table.tableEloCount + 1) == 3)
				thStr = "rd";
			
			script.appendScript("dispmsg('<h2><img src=\"" + imgsrc + "\" title=\"" + imgtitle + "\">" + whodied.getName() + 
					"</h2>','<h3>has fallen in " + (table.tableEloCount + 1) + thStr + " place.</h3>',400,2850);");
		}
		int total = 0;
		Player whowon = null;
		for (int x = 0; x < table.getMaxPlayers(); x++) {
			if (table.players[x].isPlayer()) {
				whowon = table.players[x];
				total++; 
			}
		}
		//System.out.println("PLAYERS LEFT: " + total);
		if (total <= 1) {
			int adjustedELO = 0;
			// ADJUTS ELO
			try {
				adjustedELO = (int)adjustELO(table, whowon, table.totalDeathElo / (double)table.deathEloCount, 1);
			} catch (Exception e) { 
				e.printStackTrace(); 
			}
			
			try {
				//winning bonus?
				adjustedELO+=2;
				if (!table.isRated()) {
					adjustedELO=whowon.getELO();
					//ELOch = 0;
				}
				if (whowon==null) whowon=table.getNobody();
				
				int ELOch = whowon.getELO() - adjustedELO;
				whowon.setELO(adjustedELO);
				table.gameStats.setWinningPlayerId(whowon.getOrderId());
				table.gameStats.setPlayerDeathTime(whowon.getOrderId(), new Date());
				table.gameStats.setPlayerELOChange(whowon.getOrderId(), ELOch);
				table.gameStats.setPlayerFinishPlace(whowon.getOrderId(), 0);
				table.gameStats.endGame();
				table.gameStats.save();
				String imgsrc = Rank.getRankImageSmall(whowon.getELO());
				String imgtitle = Rank.getRankName(whowon.getELO());
				script.appendScript("dispmsg('<h2><img src=\"" + imgsrc + "\" title=\"" + imgtitle + "\">" + whowon.getName() + 
						"</h2>','<h3>has won.. VICTORY.</h3>',400,2500);");
				script.appendScript("sendStatus('<h2>" + whowon.getName() + " wins.  VICTORY!</h2> Rating change: +" + 
						(-ELOch) + " to " + whowon.getELO() + "<br/><br/>');");
				script.appendScript("endGame(" + whowon.getColor() + ");");
				table.logger.logEvent(whowon.getName(), "gameend", "");
			} finally {
				table.stop();
				tables.destroyAutomatons(table);
				tables.clearRoster(table);
				tables.endGame(table);
				table.resetClock();
				if (table.isShutdown()) {
					tables.killTable(table);
					//tables.tablePlayers.remove(table);
					ScriptBuffer s = new ScriptBuffer();
					s.appendScript("push_tables(Array(" + tables.getTableNames() + "));");
					tables.sendJavascript(null, s);
				}
			}
		}

		script.appendScript("random_color(" + table.getColors() + "," + table.getDiceCounts() + ");");

		tables.sendJavascript(table, script);
		tables.updateRoster(table, 0);
	}
	   
	public void serverLogin(TableManager caller, Player newu, ScriptSession session, HttpServletRequest req, HttpSession http) {
		String IpAddr = req.getRemoteAddr();
		String userAgent = req.getHeader("user-agent");
		String referer = (String)http.getAttribute("DQOR");
			//req.getHeader("referer");
		//TODO Prevent the player from sitting multiple times
		caller.updateSQL("INSERT INTO dq_session_log (user_id,start_time,end_time,ipaddr,useragent,referer) SELECT "+newu.getDbId()+",'"+new Timestamp(new Date().getTime()).toString()+"',null,'"+IpAddr+"','"+userAgent+"','"+referer+"';");
		
		ScriptBuffer script = new ScriptBuffer();
		script.appendScript("dwr.util.byId('join').className = 'hidden';");
		script.appendScript("dwr.util.byId('joined').className = '';");
		script.appendScript("dwr.util.byId('phrase').focus();");
		script.appendScript("setMyColor(" + newu.getColor() + ");");		
		session.addScript(script);
		caller.newPlayerJoins(newu);
		newu.setIPAddr(IpAddr);
		newu.setHttpRequest(req);
		newu.setHttpSession(http);
	}
	
	public int serverChairEvent(TableManager tables, Player thisu) {
		Table table = tables.getTable(thisu);
		//table.setMaxPlayers(table.getMaxPlayers());
		//that did absolutely nothing..
		if (thisu.getELO()<table.getEloMinimum()) {
			ScriptBuffer s = new ScriptBuffer();
			s.appendScript("sendStatus('<b>You must have a rating of at least "+table.getEloMinimum()+" to sit.  Your current rating is "+thisu.getELO()+"</b><br>');");
			thisu.getSession().addScript(s);
			return 0;
		}
		if (!table.isQuiet() && thisu.isNobody() && thisu.getOrderId()<0)
			for (int x = 0; x < table.getMaxPlayers(); x++) {
				if (!table.players[x].isAdmin() && !thisu.isAdmin() && table.players[x].getIPAddr().equals(thisu.getIPAddr())) {
					ScriptBuffer s = new ScriptBuffer();
					s.appendScript("sendStatus('<b>You share an IP address with someone sitting at this table.  Sorry, only one person per table per IP in public tables.</b><br>');");
					thisu.getSession().addScript(s);
					return 0;
					
				}
			}
		if (table==null) return 0;
		
		if (thisu.getOrderId() >= 0) {
			//System.out.println("serverChairEvent: removed " + thisu.getName() + " from seat " + thisu.getOrderId());
			try {
				table.players[thisu.getOrderId()] = table.tableSeats[thisu.getOrderId()];
			} catch (Exception e) { return 0; }
			thisu.setNobody();
			table.logger.logEvent(thisu.getName(), "stood", "");

			tables.updateRoster(table, 1);
		} else
			for (int x = 0; x < table.getMaxPlayers(); x++) {
				if (table.players[x] == table.tableSeats[x] || table.players[x] == null || table.players[x].getName() == "") {
					//System.out.println("serverChairEvent: " + thisu.getName() + " seated at " + x);
					thisu.setPlayer(x);
					table.players[x] = thisu;
					thisu.setRequestStart(thisu.getRequestStart());
					table.logger.logEvent(thisu.getName(), "sat", "" + x);

					tables.updateRoster(table, 1);
					if (x >= table.getMaxPlayers()-1 && table.isNotRunning()) { ///wtf game should start :P
						//System.out.println("CHAIR EVENT CAUSES END GAME");
						//WE JSUT STARTED A START GAME GAME START 
	
						
						tables.reset(table, this);
					} else if (!table.isQuiet()){
						//broadcast this table to all eligible non playing users.
						Collection<Player> pla = tables.userSessions.keySet();
						ScriptBuffer s = new ScriptBuffer();
						s.appendScript("showPopup(1,'"+table.getParams()+"','"+table.getName()+"',"+table.getJSID()+");");
						for (Player p:pla) {
							if (p.getTable()!=table && p.isNobody() && p.getELO()>table.getEloMinimum()) {
								p.getSession().addScript(s);
							}
						}
					}

					break;
				}
			}

		return thisu.getColor();
	}
}
